package com.cqsd.ioc.core.factory;

import com.cqsd.bean.BeanUtils;
import com.cqsd.ioc.annotation.Autowrite;
import com.cqsd.ioc.annotation.Value;
import com.cqsd.ioc.core.context.BeanManager;
import com.cqsd.ioc.env.Env;
import com.cqsd.ioc.exception.FieldinjectionException;
import com.cqsd.ioc.exception.GetBeanException;
import com.cqsd.ioc.trait.Aware;
import com.cqsd.ioc.trait.BeanFactory;
import com.cqsd.ioc.trait.BeanInfo;
import com.cqsd.ioc.trait.use.BeanNameAware;
import com.cqsd.ioc.trait.use.BeanPostProcess;
import com.cqsd.ioc.trait.use.InitalizingBean;
import com.cqsd.ioc.util.AnnotationUtil;
import com.cqsd.result.Box;
import com.cqsd.string.ParseExpr;
import com.cqsd.string.StringUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author caseycheng
 * 负责创建对象
 * @date 2023/3/11-12:22
 **/
public abstract class AppFactory implements BeanFactory {
    /**
     * Bean对象的初始化前后处理器列表，这是一个全局生效的处理器，在类被扫描完毕后第一时间创建出来，并存放到这里。
     */
    protected final List<BeanPostProcess> beanPostProcessList = new ArrayList<>();
    /**
     * 日志
     */
    private static final Logger log = LogManager.getLogger(AppFactory.class);
    /**
     * 获取被子类管理的Bean信息池
     *
     * @return
     */
    @Override
    public abstract Map<String, BeanInfo> getBeanInfoMap();

    /**
     * 获取单例池
     *
     * @return
     */
    @Override
    public abstract BeanManager.SingleObjects getSingleObjects();

    /**
     * 获取beanInfo容器
     *
     * @return
     */
    @Override
    public abstract BeanManager getBeanInfoManager();

    /**
     * 获取环境变量
     *
     * @return
     */
    public abstract Env getAppEnv();

    /**
     * 对class进行加载
     *
     * @param clazz
     */
    public abstract void loadClass(Class<?> clazz);

    /**
     * 通过BeanName获取一个Bean
     *
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) {
        final var beanInfoManager = getBeanInfoManager();
        final var singleObjects = beanInfoManager.getSingleObjects();
        final var beanInfo = beanInfoManager.getBeanInfo(beanName);
        if (beanInfo == null) {
            log.error("无法获取到{},请检查是否在类上标注@Component", beanName);
            throw new GetBeanException("无法获取到" + beanName + "，请检查是否在类上标注@Component");
        }
        if (beanInfo.isSingle()) {
            var o = singleObjects.get(beanName);
            //如果单例池中没有对象
            if (o == null) {
                //创建一个再存放进去并返回
                final var bean = createBean(beanInfo);
                singleObjects.set(beanName, bean);
                return bean;
            }
            return o;
        } else {
            //多例Bean暂时不需要管理，管理的话就相当于在堆里又开了一个堆
            return createBean(beanInfo);
        }
    }

    /**
     * 通过Class类型获取一个Bean
     * 如果没找到bean就抛出异常并附带没有找到的bean
     *
     * @param type
     * @return
     */
    @SuppressWarnings("unchecked")
    @Override
    public <T> T getBean(Class<T> type) {
        //检查搜索的map和bean信息池map的一致性
        //        if (this.searchMap.size() != getBeanInfoMap().size()) {
        //            //先更新索引，再查询
        //            updateSearchMap();
        //        }
        //
        //        var info = searchMapGet(type);
        final var beanInfoManager = getBeanInfoManager();
        var info = beanInfoManager.getBeanInfo(type);
        final var singleObjects = beanInfoManager.getSingleObjects();
        if (info == null) {
            loadClass(type);
            //            info = searchMapGet(type);
            info = beanInfoManager.getBeanInfo(type);
            if (info == null) {
                log.error("无法获取到{},请检查是否在类上标注@Component", type.getName());
                throw new GetBeanException("无法获取到" + type.getName() + "，请检查是否在类上标注@Component");
            }
        }
        if (info.isSingle()) {
            var o = singleObjects.get(info.getName());
            //如果单例池中没有对象
            if (o == null) {
                //创建一个再存放进去并返回
                final var bean = createBean(info);
                singleObjects.set(info.getName(), bean);
                return (T) bean;
            }
            return (T) o;
        } else {
            //多例Bean暂时不需要管理，管理的话就相当于在堆里又开了一个堆
            return createBean(info);
        }
    }
    /**
     * 通过BeanName和Class类型获取一个Bean
     * 若两者其中有一个不满足，那么就报错
     * 暂时不实现，因为这个存储类型的map还不满足条件
     *
     * @param beanName
     * @param type
     * @return
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getBean(String beanName, Class<T> type) {
        final var infoManager = getBeanInfoManager();
        final var beanInfo = infoManager.getBeanInfo(type);

        if (!beanInfo.getName().equals(beanName)) {
            final var msg = String.format("没有在类型%s中找到beanName为%s的实例", type.getSimpleName(), beanName);
            log.error(msg);
            throw new NullPointerException(msg);
        }
        return (T) beanInfo.getBean();
    }

    /**
     * 通过BeanInfo创建一个Bean
     *
     * @param type
     * @return
     */
    @SuppressWarnings(
            "unchecked"
    )
    @Override
    public <T> T createBean(BeanInfo type) {
        final var instanceBox = Box.newEmptyBox();
        //查找无参构造器
        //如果没有就查找全参构造器
        //如果有工厂，优先使用工厂
        if (type.isFactory()) {
            log.trace("通过factory方法构建{}", type.getName());
            instanceBox.set(type.getFactory().create());
        } else {
            log.trace("通过普通方法构建{}", type.getName());
            //查询无参构造
            final var constructorOptional = Arrays.stream(type.getAllConstructor())
                    .filter(constructor -> constructor.getParameters().length == 0)
                    .findFirst();

            constructorOptional.ifPresentOrElse(
                    constructor -> {
                        instanceBox.set(createNoArgsConstructor(type, constructor));
                    },
                    () -> {
                        final Constructor<?> constructor = BeanUtils.findAllArgsConstructor(type.getAllConstructor());
                        instanceBox.set(createAllArgsConstructor(type, constructor));
                    });

        }
        var instance = instanceBox.get();
        instance = hookProcess(type, instance);
        type.setBean(instance);
        return (T) instance;
    }


    /**
     * 通过全参构造器来创建对象
     *
     * @param type        bean类型
     * @param constructor 全参构造器
     * @return 创建完毕的对象
     */
    private Object createAllArgsConstructor(BeanInfo type, Constructor<?> constructor) {
        final Class<?>[] types = constructor.getParameterTypes();
        List<Object> constructorArgs = new ArrayList<>();
        Object instance;
        if (constructor.isAnnotationPresent(Autowrite.class)) {
            final var parameter = constructor.getAnnotation(Autowrite.class).value();
            for (int i = 0, parameterLength = parameter.length; i < parameterLength; i++) {
                Class<?> implClass = parameter[i];
                if (!types[i].isAssignableFrom(implClass)) {
                    final var msg = String.format("%s与%s不兼容，请检查@Autowrite里class是否与参数顺序兼容",
                            implClass.getSimpleName(),
                            types[i].getSimpleName());
                    log.error(msg);
                    throw new InstantiationError(msg);
                }
                constructorArgs.add(getBean(implClass));
            }
        } else {
            for (Class<?> t : types) {
                constructorArgs.add(getBean(t));
            }
        }
        try {
            instance = constructor.newInstance(constructorArgs.toArray());
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            log.error("依赖条件暂不满足,原因是:\r{}", e.getMessage());
            throw new RuntimeException("依赖条件暂不满足，原因是:", e);
        }
        return instance;
    }

    /**
     * 通过无参构造来创建对象
     *
     * @param type
     * @param constructor
     * @return
     */
    private Object createNoArgsConstructor(BeanInfo type, Constructor<?> constructor) {
        Object instance;
        try {
            instance = constructor.newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return instance;
    }

    /**
     * 对创建后的对象来一次生命周期
     * {@link BeanPostProcess}接口里的afterProcessBeforeInitalizing方法可以对对象进行代理操作
     *
     * @param instance
     * @return
     */
    protected Object hookProcess(BeanInfo type, Object instance) {
        //检查是否是BeanName回调函数
        if (Aware.class.isAssignableFrom(type.getType())) {
            if (instance instanceof BeanNameAware beanNameAware) {
                beanNameAware.setName(type.getName());
            }
        }
        //运行初始化前处理器
        for (BeanPostProcess process : beanPostProcessList) {
            instance = process.postProcessBeforeInitalizing(type.getName(), instance);
        }
        //初始化字段
        initField(type, instance);
        //对@value注释的内容进行初始化，仅支持最基本的参数，不能初始化对象。
        initProperties(type, instance);
        //运行自定义的初始化程序
        if (instance instanceof InitalizingBean initalizingBean) {
            log.trace("正在为{}运行自定义的bean初始化方法", type.getType().getName());
            initalizingBean.afterPropertiesSet();
        }
        //运行初始化后置处理器
        for (BeanPostProcess process : beanPostProcessList) {
            instance = process.afterProcessBeforeInitalizing(type.getName(), instance);
        }
        //最终返回实例
        return instance;

    }

    /**
     * 对属性进行注入，例如 {@link Value }里面的表达式，使用ParseExpr来进行解析
     * StringUtil里有String对象的映射器
     */
    private void initProperties(BeanInfo type, Object instance) {
        //过滤出需要被注入的字段
        List<Field> values = AnnotationUtil.filterAnnotation(type.getAllField(), Value.class);
        if (values.size() == 0) {
            return;
        }
        Value value;
        ParseExpr expr;
        String fileValue;
        try {
            for (Field field : values) {
                //获取字段上的Value注解
                value = field.getAnnotation(Value.class);
                //创建解析对象
                expr = new ParseExpr(value.value());
                //开始解析
                expr.parse();
                //获取结果
                //判断是否为表达式，如果是表达式就从Env中获取，如果不是表达式就直接赋值
                if (expr.hasExpr()) {
                    fileValue = getAppEnv().getProperties(expr.result());
                } else {
                    fileValue = expr.result();
                }
                //通过字段类型来适应内容，仅限于简单类型
                final Object o;
                try {
                    o = StringUtil.str_map(fileValue, field.getType());
                } catch (RuntimeException e) {
                    final var msg = String.format("被注入的字段类型%s与无法注入路径为%s的数据:%s",
                            field.getType(),
                            expr.result(),
                            fileValue);
                    log.error(msg, e);
                    throw new FieldinjectionException();
                }
                field.setAccessible(true);
                //注入
                field.set(instance, o);
            }
        } catch (IllegalAccessException e) {
            final var msg = String.format("字段注入出现未知异常:\r%s", e.getMessage());
            log.error(msg, e);
            throw new RuntimeException(msg, e);
        }

    }


    /**
     * 对字段和方法进行注入
     *
     * @param type     bean描述
     * @param instance 对应的Bean对象
     */
    private void initField(BeanInfo type, Object instance) {
        final List<Field> fields = AnnotationUtil.filterAnnotation(type.getAllField(), Autowrite.class);
        final List<Method> methods = AnnotationUtil.filterAnnotation(type.getAllMethods(), Autowrite.class);
        try {
            for (Field field : fields) {
                final var autowrite = field.getAnnotation(Autowrite.class);
                field.setAccessible(true);
                if (autowrite.value()[0] != Object.class) {
                    final Class<?> implClass = autowrite.value()[0];
                    final Class<?> fType = field.getType();
                    if (fType.isAssignableFrom(implClass)) {
                        //如果与接口兼容，则获取这个bean
                        field.set(instance, getBean(implClass));
                    } else {
                        final var errMsg = String.format("%s与%s不兼容，请检查@Autowrite内的参数顺序",
                                fType.getSimpleName(),
                                implClass.getSimpleName()
                        );
                        log.error(errMsg);
                        throw new InstantiationError(errMsg);
                    }
                } else {
                    field.set(instance, getBean(field.getType()));
                }
            }
        } catch (IllegalAccessException e) {
            final var msg = type.getName() + "\r没有满足自动装配条件";
            log.error(msg);
            throw new RuntimeException(msg, e);
        }
        try {
            for (Method method : methods) {
                //获取方法需要的参数，然后注入
                final var types = method.getParameterTypes();
                //如果方法中的注解第一个不是默认的Object.class就代表需要查找接口
                Object[] args;
                final var autowrite = method.getAnnotation(Autowrite.class);
                if (autowrite.value()[0] != Object.class) {
                    args = Arrays.stream(autowrite.value()).map(this::getBean).toArray();
                } else {
                    args = Arrays.stream(types).map(this::getBean).toArray();
                }
                method.invoke(instance, args);
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            final var msg = type.getName() + "\r没有满足自动装配条件";
            log.error(msg);
            throw new RuntimeException(msg, e);
        }
    }

    /**
     * 查找Bean定义
     *
     * @param type
     * @return
     */
    @Override
    public Optional<? extends BeanInfo> searchBeanDef(Class<?> type) {
        final var info = getBeanInfoManager().getBeanInfo(type);
        if (info == null) {
            return Optional.empty();
        } else {
            return Optional.of(info);
        }
    }

    /**
     * 将自己解析的bean交给容器管理
     *
     * @param beanInfo
     */
    @Override
    abstract public void registerBean(BeanInfo beanInfo);

    /**
     * 将解析的bean集合交给容器管理
     *
     * @param beanInfoCollection
     */
    @Override
    abstract public void registerBeans(Collection<BeanInfo> beanInfoCollection);
}
